/**
 * @file   Client.h
 * @author Djuro Drljaca (djurodrljaca@gmail.com)
 * @date   2012-09-08
 * @brief  DataBus Client.
 *
 * Copyright (C) 2013  Djuro Drljaca <djurodrljaca@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef LIBDATABUS_SERVER_CLIENT_H
#define LIBDATABUS_SERVER_CLIENT_H

#include <DataBus/Common/Common.h>
#include <DataBus/Common/Packet.h>
#include <DataBus/Common/PacketParser.h>
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtCore/QList>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtNetwork/QTcpSocket>

namespace DataBus
{
/**
 * @brief The Client class
 */
class DATABUSSHARED_EXPORT Client : public QObject
{
    Q_OBJECT

public:
    /**
     * @brief Constructor for Client
     * @param parent
     */
    explicit Client(QObject *parent);

    /**
     * @brief Destructor for Client
     */
    ~Client();

    /**
     * @brief Initializes the Client
     *
     * @param socket    TCP socket that will be used to communicate to the client
     *
     * @retval true     Success
     * @retval false    Error
     */
    bool initialize(QTcpSocket *socket);

    /**
     * @brief Closes the Client
     */
    void close();

    /**
     * @brief Returns Client ID
     *
     * @return Client ID
     */
    quint8 getClientId() const;

    /**
     * @brief Returns Activity Timeout
     *
     * @return Activity Timeout in seconds
     */
    quint8 getActivityTimeout() const;

    /**
     * @brief Returns Ping Response Timeout
     *
     * @return Ping Response Timeout in seconds
     */
    quint8 getPingResponseTimeout() const;

    /**
     * @brief Returns Ping Retry Count
     *
     * @return Ping Retry Count
     */
    quint8 getPingRetryCount() const;

    /**
     * @brief Register Client
     *
     * @param clientId              Client ID
     * @param activityTimeout       Activity Timeout
     * @param pingResponseTimeout   Ping Response Timeout
     * @param pingRetryCount        Ping Retry Count
     *
     * @retval true     Success
     * @retval false    Error
     */
    bool registerClient(const quint8 clientId,
                        const quint8 activityTimeout,
                        const quint8 pingResponseTimeout,
                        const quint8 pingRetryCount);

    /**
     * @brief Checks if client is registered
     *
     * @retval true     Client is registered
     * @retval false    Client is not registered
     */
    bool isRegistered() const;

    /**
     * @brief Checks if client is active
     *
     * @retval true     Client is active
     * @retval false    Client is not active
     */
    bool isActive() const;

    /**
     * @brief Tells if at least one packet is available
     *
     * @retval true     At least one packet is avalable
     * @retval false    No packets are avalable
     */
    bool packetsAvailable() const;

    /**
     * @brief Takes one packet from the packet list (if available) and returns it
     *
     * @return Packet
     *
     * @note User should make sure that there is at least one packet available by checking packetsAvailable()
     */
    Packet takePacket();

    /**
     * @brief Sends a DataBus Packet to the client
     *
     * @param packet DataBus Packet
     *
     * @retval true     Success
     * @retval false    Error
     */
    bool sendPacket(const Packet &packet) const;

    /**
     * @brief Generates next Packet ID for this client
     *
     * @return Packet ID
     */
    quint8 getNextPacketId();

signals:
    /**
     * @brief This signal notifies that the DataBus Client has disconnected from the DataBus Server
     *
     * @param client    Pointer to this Client object
     */
    void clientDisconnected(Client *client);

    /**
     * @brief This signal notifies that the DataBus Client has received at least one new packet
     *
     * @param client    Pointer to this Client object
     */
    void newPacketReceived(Client *client);

private slots:
    /**
     * @brief This slot should be executed when TCP socket connection has disconnected
     */
    void disconnected();

    /**
     * @brief This slot should be executed when there is data available to be read from the socket
     */
    void readData();

    /**
     * @brief This slot processes clients activity (ping)
     */
    void processActivity();

private:
    /**
     * @brief The Status enum
     */
    enum Status
    {
        Status_Unregisterd = 0,     /**< Client is still not registered  */
        Status_Active,              /**< Client is active                */
        Status_WaitForPingResponse, /**< Waiting for Ping Response       */
        Status_Inactive             /**< Client is inactive              */
    };

    /**
     * @brief Disable Client copy constructor
     */
    Client(const Client &);

    /**
     * @brief Disable Client operator =
     *
     * @return Nothing
     */
    Client &operator=(const Client &);

    /**
     * @brief Processes received packets
     */
    void processReceivedPackets();

    /**
     * @brief Sends a Ping Request
     *
     * @retval  true    Success
     * @retval  false   Error
     */
    bool sendPingRequest();

    /**
     * @brief TCP socket
     */
    QTcpSocket *m_socket;

    /**
     * @brief Client ID
     */
    quint8 m_clientId;

    /**
     * @brief Activity Timeout
     */
    int m_activityTimeout;

    /**
     * @brief Ping Response Timeout
     */
    int m_pingResponseTimeout;

    /**
     * @brief Ping Retry Count
     */
    quint8 m_pingRetryCount;

    /**
     * @brief Packet parser
     */
    PacketParser m_packetParser;

    /**
     * @brief A list of received packets
     */
    QList<Packet> m_packetList;

    /**
     * @brief Used for generation of Packet IDs
     */
    quint8 m_packetIdCounter;

    /**
     * @brief Timer component
     */
    QTimer *m_timer;

    /**
     * @brief Client status
     */
    Status m_status;

    /**
     * @brief Measures activity timeout
     */
    QTime m_activityTimeoutTimer;

    /**
     * @brief Measures ping response timeout
     */
    QTime m_pingResponseTimeoutTimer;

    /**
     * @brief Ping Retry Count Counter
     */
    quint8 m_pingRetryCountCounter;

    /**
     * @brief Holds the last Ping Request Packet ID
     */
    quint8 m_lastPingRequestPacketId;
};
}

#endif // LIBDATABUS_SERVER_CLIENT_H
